/*
 *****************************************************************************
 * Copyright 2004-2012 Aiti Ltd. All rights reserved.
 *****************************************************************************
 *  Change Activity:
 *  --------------------------------------------------------------------------
 *  Date      Author          Description
 *  --------------------------------------------------------------------------
 *  Jan 14, 2013 ThinhNV2  Initial Code drop.
 *****************************************************************************
 */
package com.aiti.core.c1202m.logger.trace;

import java.io.PrintWriter;
import java.io.StringWriter;

import com.aiti.c1202m.core.logger.AbstractLoggerFactory;
import com.aiti.c1202m.core.logger.IfLogger;

/**
 * <H3>
 * Logging Utility Class.
 * </H3>
 * 
 * ex. 
 * <code>
 * 	LogUtil.writeLog(...);
 * </code>
 * @author thinhnv2
 * @since 2013/01/14
 */
public class LogUtil {
	/** infomation level */
	public static final int INF_LOG = 0;
	/** error level */
	public static final int ERR_LOG = 1;
	/** debug level */
	public static final int DBG_LOG = 2;
	/** warning level */
	public static final int WARN_LOG = 3;

	/** logger factory */
	private static AbstractLoggerFactory loggerFactory = AbstractLoggerFactory.getLoggerFactory();

	//***** Constructor *****
	//***** Public method *****
	/**
	 * Log Output.
	 * <p>
	 * gets default IfLogger and output log.<br>
	 * logging level are follows.<br>
	 * <ul>
	 * <li>infomation log(LEVEL:0)
	 * <li>error log(LEVEL:1)
	 * <li>debug log(LEVEL:2)
	 * <li>warning log(LEVEL:3)
	 * </ul><br>
	 * if specified level is not abave, do nothing.
	 * 
	 * @param className - Class name 
	 * @param message - Message
	 * @param level - Logging level
	 */
	public static void writeLog(String className, String message, int level) {
		writeLog(loggerFactory.getLogger(className), message, level);
	}

	/**
	 * Log Output dedicated to the middleware layer's package (ex "com.nsk.base.mid").<br>
	 * <p>
	 * Messages is formatted by a service layer's class "com.nsk.base.biz.service.common.util.LogWrapper".<br>
	 * But a middleware layer can't call the service layer, so the middleware layer calls this same layer's method instead.<br>
	 * <p>
	 * @param className - Class name 
	 * @param methodName - Method name 
	 * @param message - Message
	 * @param level - Logging level
	 */
	public static void writeMiddlewareLog(String className, String methodName, String message, int level) {
		writeLog(loggerFactory.getLogger(className), getFormattedMessage(className, methodName, message), level);
	}

	/**
	 * Stack Trace output dedicated to the middleware layer's package (ex "com.nsk.base.mid").<br>
	 * <p>
	 * Messages is formatted by a service layer's class "com.nsk.base.biz.service.common.util.LogWrapper".<br>
	 * But a middleware layer can't call the service layer, so the middleware layer calls this same layer's method instead.<br>
	 * <p>
	 * @param className - Class name 
	 * @param methodName - Method name 
     * @param t - exception object.
	 */
	public static void writeMiddlewareStack(String className, String methodName, Throwable t) {
		writeStack(loggerFactory.getLogger(className), getFormattedMessage(className, methodName, ""), t, ERR_LOG);
	}

    /**
     * Stack Trace output dedicated to the middleware layer's package (ex "com.nsk.base.mid").<br>
     * <p>
     * Messages is formatted by a service layer's class "com.nsk.base.biz.service.common.util.LogWrapper".<br>
     * But a middleware layer can't call the service layer, so the middleware layer calls this same layer's method instead.<br>
     * <p>
     * @param className - Class name 
     * @param methodName - Method name 
     * @param t - exception object.
     * @param level - Logging level
     */
    public static void writeMiddlewareStack(String className, String methodName, Throwable t, int level) {
        writeStack(loggerFactory.getLogger(className), getFormattedMessage(className, methodName, ""), t, level);
    }

	/**
	 * return true when the logger for the specified class is debugEnabled.
	 * @param className - Class name.
	 * @return true when the logger for the specified class is debugEnabled.
	 */
	public static boolean isDebugEnabledFor(String className) {
		IfLogger _logger = loggerFactory.getLogger(className);
		return _logger.isDebugEnabled();
	}
	/**
	 * 
	 * <br>
	 * @param e
	 * @return
	 */
    public static String convertStackToString(String message, Throwable t) {
    	if(t == null){
    		return message;
    	}
        StringWriter _writer = new StringWriter();
        PrintWriter _pw = new PrintWriter(_writer);
        if(message != null && !"".equals(message)){
        	_pw.write(message);
        	_pw.write(" ");
        }
        t.printStackTrace(_pw);
        return removeLastCRLF(_writer.toString());
    }

	//***** Protected method *****
	//***** Private method *****
	/**
	 * format message. <br>
	 * @param className - Class name.
	 * @param methodName - Method name.
	 * @param message - Message
     * @return formatted message.
	 */
	private static String getFormattedMessage(String className, String methodName, String message) {
		StringBuffer _buffer = new StringBuffer();
		_buffer.append(getUnqalifiedClassName(className)).append(".");
		_buffer.append(methodName).append(":");
		_buffer.append(message);
		return _buffer.toString();
	}
	
	/**
	 * write log.
	 * <p>
	 * logging level are follows.
	 * <p>
	 * @param logger IfLogger
	 * @param message out put message
	 * @param level Logging level
	 */
	private static void writeLog(IfLogger logger, String message, int level) {
		//write log
		switch (level) {
			case INF_LOG :
				logger.info(message);
				break;
			case ERR_LOG :
				logger.error(message);
				break;
			case DBG_LOG :
				if(logger.isDebugEnabled()) logger.debug(message);
				break;
			case WARN_LOG :
				logger.warn(message);
				break;
			default :
				//do nothing
				break;
		}		
	}//end writeLog()

	/**
	 * write stack trace.
	 * <br>
	 * @param logger IfLogger
	 * @param message out put message
	 * @param t exception object.
	 */
	private static void writeStack(IfLogger logger, String message, Throwable t,int level) {
		//write log
		switch (level) {
			case INF_LOG :
				logger.info(convertStackToString(message, t));
				break;
			case ERR_LOG :
				logger.error(convertStackToString(message, t));
				break;
			case DBG_LOG :
				if(logger.isDebugEnabled()) logger.debug(convertStackToString(message, t));
				break;
			case WARN_LOG :
				logger.warn(convertStackToString(message, t));
				break;
			default :
				//do nothing
				break;
		}		
	}//end writeStack()

	
	/**
	 * getUnqalifiedClassName
	 * <br>
	 * @param fullQualifiedClassName String
	 * @return ClassName
	 */
	private static String getUnqalifiedClassName(String fullQualifiedClassName) {
		int _dotPos = fullQualifiedClassName.lastIndexOf('.');
		if (_dotPos == -1) {
			return fullQualifiedClassName;
		} else {
			return fullQualifiedClassName.substring(_dotPos + 1);
		}
	}
	/**
	 * CR
	 */
	private static final char LF = '\n';
	/**
	 * LF
	 */
	private static final char CR = '\r';
	/**
     * <p>Removes one newline from end of a String if it's there,
     * otherwise leave it alone.  A newline is &quot;<code>\n</code>&quot;,
     * &quot;<code>\r</code>&quot;, or &quot;<code>\r\n</code>&quot;.</p>
     * <pre>
     * removeLastCRLF(null)          = null
     * removeLastCRLF("")            = ""
     * removeLastCRLF("abc \r")      = "abc "
     * removeLastCRLF("abc\n")       = "abc"
     * removeLastCRLF("abc\r\n")     = "abc"
     * removeLastCRLF("abc\r\n\r\n") = "abc\r\n"
     * removeLastCRLF("abc\n\r")     = "abc\n"
     * removeLastCRLF("abc\n\rabc")  = "abc\n\rabc"
     * removeLastCRLF("\r")          = ""
     * removeLastCRLF("\n")          = ""
     * removeLastCRLF("\r\n")        = ""
     * </pre>
	 * 
	 * <br>
	 * @param string
	 * @return
	 */
	private static String removeLastCRLF(String string) {
		if (string == null || "".equals(string)) {
			return string;
		}
		if (string.length() == 1) {
			char _c = string.charAt(0);
			if (_c == CR || _c == LF) {
				return "";
			}
			return string;
		}

		int _lastIdx = string.length() - 1;
		char _last = string.charAt(_lastIdx);

		if (_last == LF) {
			if (string.charAt(_lastIdx - 1) == CR) {
				_lastIdx--;
			}
		} else if (_last != CR) {
			_lastIdx++;
		}
		return string.substring(0, _lastIdx);
	}

	//	***** Call back methods *****
	//	***** Getter and Setter *****
}
